package com.taglab.chic.mvc.spring;

import java.io.IOException;
import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.Ordered;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;

/**
 * @author Markus Kobler
 * @author Peter O'Shaughnessy
 */
public class RedirectHandlerMapping extends AbstractHandlerMapping {

  private final Logger logger = LoggerFactory.getLogger( this.getClass() );

  /**
   * Redirects may be defined by Properties. This allows us to define them easily using a
   * Spring bean property.
   */
  private Properties redirects;
  
  /**
   * Redirects may also be defined in a separate file. This has the advantage that changes can be reloaded
   * without a restart (we use a FileChangedReloadingStrategy).
   */
  private String reloadableRedirectsPath;
  private PropertiesConfiguration reloadableRedirects;

  
  public RedirectHandlerMapping() {
    // defaults to first handler
    setOrder(Ordered.HIGHEST_PRECEDENCE);
  }  
  

  public void setRedirects(Properties redirects) {
    this.redirects = redirects;
  }
  
  public void setReloadableRedirectsPath(String reloadableRedirectsPath) {
    this.reloadableRedirectsPath = reloadableRedirectsPath;  
  }
  
  public void setReloadableRedirects(PropertiesConfiguration reloadableRedirects) {
    this.reloadableRedirects = reloadableRedirects;
  }
  
  /**
   * This can't be done when the reloadableRedirectsPath property is first set 
   * because it won't have access to the application context. 
   */
  public void initialiseReloadableRedirects() {
    
    Resource reloadableRedirectsResource = getApplicationContext().getResource( reloadableRedirectsPath );
    
    if( !reloadableRedirectsResource.exists() ) {
      logger.error( "Couldn't find redirects properties at file path: {}", reloadableRedirectsPath );
      return;
    }
      
    try {
    
      logger.debug( "Initialising reloadable redirects from: {}", reloadableRedirectsPath );
      
      reloadableRedirects = new PropertiesConfiguration( reloadableRedirectsResource.getFile() );
      reloadableRedirects.setReloadingStrategy( new FileChangedReloadingStrategy() );
      
    } catch( IOException ioex ) {
      logger.error( "Error reading redirect properties file", ioex );
    } catch( ConfigurationException cex ) {
      logger.error( "Redirect configuration error", cex );
    }
    
  }

  @Override
  protected Object getHandlerInternal(HttpServletRequest request) throws Exception {

    if( reloadableRedirectsPath != null && reloadableRedirects == null ) initialiseReloadableRedirects();
    
    return lookupRedirect(request);
  
  }


  protected RedirectHandler lookupRedirect(HttpServletRequest request ) {

    String destination = null;
    
    if( redirects != null ) {
      destination = redirects.getProperty(request.getRequestURI());  
    }
    
    if( destination == null && reloadableRedirects != null ) {
      destination = (String)reloadableRedirects.getString(request.getRequestURI());
    }
    
    if( destination != null ) {
      logger.debug( "Found redirect '{}' for URI '{}'", destination, request.getRequestURI() );
      return new RedirectHandler(destination);
    }
    
    return null;
  }


  /**
   *
   * XXX Add support for perm redirects, codes etc
   */
  private class RedirectHandler implements HttpRequestHandler {

    private String destination;

    private RedirectHandler(String destination) {
      this.destination = destination;
    }

    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      response.sendRedirect(destination);
    }

  }

}